package org.yzh.web.endpoint;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Snowflake;
import io.github.yezhihao.netmc.core.annotation.Async;
import io.github.yezhihao.netmc.core.annotation.AsyncBatch;
import io.github.yezhihao.netmc.core.annotation.Endpoint;
import io.github.yezhihao.netmc.core.annotation.Mapping;
import io.github.yezhihao.netmc.session.Session;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import org.yzh.commons.util.JsonUtils;
import org.yzh.protocol.basics.JTMessage;
import org.yzh.protocol.commons.JT808;
import org.yzh.protocol.t808.*;
import org.yzh.web.model.entity.Device;
import org.yzh.web.model.entity.DeviceDO;
import org.yzh.web.model.enums.SessionKey;
import org.yzh.web.service.FileService;

import javax.annotation.Resource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static org.yzh.protocol.commons.JT808.*;

@Endpoint
@Component
public class JT808Endpoint {

    private static final Logger log = LoggerFactory.getLogger(JT808Endpoint.class);
    private static final String createStr = "(\n" +
            "  `id` bigint(11) NOT NULL,\n" +
            "  `client_id` varchar(20) DEFAULT NULL COMMENT '设备ID',\n" +
            "  `warn_bit` int(10) DEFAULT NULL COMMENT '报警标志',\n" +
            "  `status_bit` int(2) DEFAULT NULL COMMENT '状态',\n" +
            "  `latitude` double(10,6) DEFAULT NULL COMMENT '纬度',\n" +
            "  `longitude` double(10,6) DEFAULT NULL COMMENT '经度',\n" +
            "  `altitude` int(10) DEFAULT NULL COMMENT '高程(米)',\n" +
            "  `speed` int(10) DEFAULT NULL COMMENT '速度(1/10公里每小时)',\n" +
            "  `direction` int(10) DEFAULT NULL COMMENT '方向',\n" +
            "  `device_time` varchar(20) DEFAULT NULL COMMENT 'BCD时间(YYMMDDHHMMSS)',\n" +
            "  `track_spacing` bigint(11) DEFAULT NULL COMMENT '轨迹间隔(单位秒)',\n" +
            "  `tenant_id` varchar(12) DEFAULT '000000' COMMENT '租户ID',\n" +
            "  `create_user` bigint(20) DEFAULT NULL COMMENT '创建人',\n" +
            "  `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',\n" +
            "  `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n" +
            "  `update_user` bigint(20) DEFAULT NULL COMMENT '修改人',\n" +
            "  `update_time` datetime DEFAULT NULL COMMENT '修改时间',\n" +
            "  `is_deleted` int(2) DEFAULT '0' COMMENT '是否已删除',\n" +
            "  PRIMARY KEY (`id`),\n" +
            "  KEY `client_id` (`client_id`)\n" +
            ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";

    @Autowired
    private FileService fileService;
    @Resource
    JdbcTemplate jdbcTemplate;
    @Autowired
    private Snowflake snowflake;

    @Mapping(types = 终端通用应答, desc = "终端通用应答")
    public Object T0001(T0001 message, Session session) {
        session.response(message);
        return null;
    }

    @Mapping(types = 终端心跳, desc = "终端心跳")
    public void T0002(JTMessage message, Session session) {
    }

    @Mapping(types = 终端注销, desc = "终端注销")
    public void T0003(JTMessage message, Session session) {
        session.invalidate();
    }

    @Mapping(types = 查询服务器时间, desc = "查询服务器时间")
    public T8004 T0004(JTMessage message, Session session) {
        T8004 result = new T8004(LocalDateTime.now(ZoneOffset.UTC));
        return result;
    }

    @Mapping(types = 终端补传分包请求, desc = "终端补传分包请求")
    public void T8003(T8003 message, Session session) {
    }

    @Mapping(types = 终端注册, desc = "终端注册")
    public T8100 T0100(T0100 message, Session session) {
        session.register(message);
        DeviceDO device = new DeviceDO();
        device.setProtocolVersion(message.getProtocolVersion());
        device.setMobileNo(message.getClientId());
        device.setDeviceId(message.getDeviceId());
        device.setPlateNo(message.getPlateNo());
        session.setAttribute(SessionKey.Device, device);

        T8100 result = new T8100();
        result.setResponseSerialNo(message.getSerialNo());
        result.setToken(message.getDeviceId() + "," + message.getPlateNo());
        result.setResultCode(T8100.Success);
        //检查设备在后台管理是否存在
        if (isExistDevice(message.getClientId())) {
            //存在则更新设备相关信息
            String updateSql = "update ryl_device t set t.province_id = " + message.getProvinceId() +
                    ", t.city_id= " + message.getCityId() +
                    ", t.car_number= " + "'" + message.getPlateNo() + "'" +
                    ", t.plate_color= " + message.getPlateColor() +
                    " where t.equipment_no= " + message.getClientId();
            //添加设备运行记录
            jdbcTemplate.update(updateSql);

        }
        return result;
    }

    @Mapping(types = 终端鉴权, desc = "终端鉴权")
    public T0001 T0102(T0102 message, Session session) {
        session.register(message);
        DeviceDO device = new DeviceDO();
        String[] token = message.getToken().split(",");
        device.setProtocolVersion(message.getProtocolVersion());
        device.setMobileNo(message.getClientId());
        device.setDeviceId(token[0]);
        if (token.length > 1)
            device.setPlateNo(token[1]);
        session.setAttribute(SessionKey.Device, device);

        T0001 result = new T0001();
        result.setResponseSerialNo(message.getSerialNo());
        result.setResponseMessageId(message.getMessageId());
        result.setResultCode(T0001.Success);
        return result;
    }

    @Mapping(types = 查询终端参数应答, desc = "查询终端参数应答")
    public void T0104(T0104 message, Session session) {
        session.response(message);
    }

    @Mapping(types = 查询终端属性应答, desc = "查询终端属性应答")
    public void T0107(T0107 message, Session session) {
        session.response(message);
    }

    @Mapping(types = 终端升级结果通知, desc = "终端升级结果通知")
    public void T0108(T0108 message, Session session) {
    }

    /**
     * 异步批量处理
     * poolSize：参考数据库CPU核心数量
     * maxElements：最大累积4000条记录处理一次
     * maxWait：最大等待时间1秒
     */
    @AsyncBatch(poolSize = 2, maxElements = 4000, maxWait = 1000)
    @Mapping(types = 位置信息汇报, desc = "位置信息汇报")
    public void T0200(List<T0200> list) {
        if (list != null && list.size() > 0) {
            T0200 t = list.get(0);
            Map<Integer, Object> map = t.getAttributes();
            if (map != null && map.containsKey(235)) {
                Object alarmId = map.get(235);
                if (Objects.nonNull(alarmId)) {
                    //Object 转map
                    String alarmIdMap = JsonUtils.toJson(alarmId);
                    log.info("扩展信息235中数据为：" + alarmIdMap);
                }
            }
        }
        //动态建表，把定位信息按天进行存储
        String tableDay = "ryl_device_run_his" + DateUtil.format(new Date(), "yyyyMMdd");
        String createRunHis = "CREATE TABLE IF NOT EXISTS " + tableDay + createStr;
        jdbcTemplate.execute(createRunHis);
        try {
            if (CollectionUtil.isNotEmpty(list)) {
                DecimalFormat df = new DecimalFormat("0.000000");
                for (T0200 t0200 : list) {
                    String longitude = df.format((float) t0200.getLongitude() / 1000000);
                    String latitude = df.format((float) t0200.getLatitude() / 1000000);
                    String equipmentNo = t0200.getClientId();//设备号
                    Map<Integer, Object> map = t0200.getAttributes();//总里程
                    Integer totalMileage = null;
                    if (CollectionUtil.isNotEmpty(map) && map.containsKey(1)) {
                        totalMileage = Integer.parseInt(String.valueOf(map.get(1)));
                    }

                    int altitude = t0200.getAltitude(); //高程
                    int direction = t0200.getDirection(); //风向
                    int speed = t0200.getSpeed(); //风向
                    if (!isExistDevice(equipmentNo)) {
                        continue;
                    }
                    Integer status = 1; //设备状态(0未用 1在线 2静止 3离线 4注销 5到期)
                    Integer accStatus = 0; //acc开关状态 0关 1开
                    if (t0200.getStatusBit() > 0) {
                        accStatus = 1;
                    }
                    if (speed <= 0) {
                        status = 2;
                    }

                    //修改设备实时经纬度
                    String updateSql = "update ryl_device t set t.longitude = " + longitude +
                            ", t.latitude= " + latitude + " " +
                            ", t.altitude= " + altitude + " " +
                            ", t.direction= " + direction + " " +
                            ", t.speed= " + speed + " " +
                            ", t.total_mileage= " + totalMileage + " " +
                            ", t.positioning_time= " + "'" + DateUtil.format(t0200.getDeviceTime(), "yyyy-MM-dd HH:mm:ss") + "' " +
                            ", t.signal_time= " + "'" + DateUtil.format(t0200.getDeviceTime(), "yyyy-MM-dd HH:mm:ss") + "' " +
                            ", t.status= " + status + " " +
                            ", t.acc_status= " + accStatus + " " +
                            "where t.equipment_no= " + equipmentNo;

                    //添加设备运行记录
                    jdbcTemplate.update(updateSql);
                    Long id = snowflake.nextId();
                    String insertSql = "insert into " + tableDay + "(id, client_id, longitude, latitude, device_time, warn_bit, status_bit, altitude, direction, speed) " +
                            "values (" + id + "," + t0200.getClientId() + "," + longitude + "," + latitude + ",'"
                            + DateUtil.format(t0200.getDeviceTime(), "yyyy-MM-dd HH:mm:ss") + "',"
                            + t0200.getWarnBit() + "," + t0200.getStatusBit() + "," + altitude + "," + direction + "," + t0200.getSpeed()
                            + ")";

                    //添加设备运行记录
                    jdbcTemplate.update(insertSql);
                }
            }
        } catch (Exception e) {
            log.error("位置信息汇报保存异常，{}", e);
        }

    }

    /**
     * 判断设备是否已经录入管理后台，未录入则不保存设备数据
     *
     * @param equipmentNo
     * @return
     */
    private boolean isExistDevice(String equipmentNo) {
        String querySql = "select id from ryl_device where is_deleted = 0 and equipment_no = " + equipmentNo;
        String insertSql = "insert into ryl_device_unknown (id, client_id) " +
                "values (" + snowflake.nextId() + "," + equipmentNo + ")";
        try {
            Map<String, Object> map = jdbcTemplate.queryForMap(querySql);
            if (CollectionUtil.isEmpty(map)) {
                System.out.println("存在未纳入管理后端的设备信息,设备号为：" + equipmentNo);
                jdbcTemplate.update(insertSql);
                return false;
            }
        } catch (EmptyResultDataAccessException e) {
            System.out.println("存在未纳入管理后端的设备信息,设备号为：" + equipmentNo);
            jdbcTemplate.update(insertSql);//此功能后期可根据实际情况是否保留
            return false;
        }
        return true;
    }

    private boolean hasResults(String sql) {
        Device result = jdbcTemplate.queryForObject(sql, new RowMapper<Device>() {
            @Override
            public Device mapRow(ResultSet rs, int rowNum) throws SQLException {
                return new Device(rs.getInt("id"));
            }
        });
        return result != null;  // 如果result不为null，则表示有结果
    }

    @Mapping(types = 定位数据批量上传, desc = "定位数据批量上传")
    public void T0704(T0704 message) {
        //0200 00630145 4173302863bf000000000000000301b20f200725e57800420000006b231222155125010400000cfa30011f310114eb39000c00b28986042916189065840100060089ffffffff000600c5ffffffef0006002d00003214001100d5383636353736303730363831373439ca7e

    }

    @Mapping(types = {位置信息查询应答, 车辆控制应答}, desc = "位置信息查询应答/车辆控制应答")
    public void T0201_0500(T0201_0500 message, Session session) {
        session.response(message);
    }

    @Mapping(types = 事件报告, desc = "事件报告")
    public void T0301(T0301 message, Session session) {
    }

    @Mapping(types = 提问应答, desc = "提问应答")
    public void T0302(T0302 message, Session session) {
    }

    @Mapping(types = 信息点播_取消, desc = "信息点播/取消")
    public void T0303(T0303 message, Session session) {
    }

    @Mapping(types = 查询区域或线路数据应答, desc = "查询区域或线路数据应答")
    public void T0608(T0608 message, Session session) {
        session.response(message);
    }

    @Mapping(types = 行驶记录数据上传, desc = "行驶记录仪数据上传")
    public void T0700(T0700 message, Session session) {
        session.response(message);
    }

    @Mapping(types = 电子运单上报, desc = "电子运单上报")
    public void T0701(JTMessage message, Session session) {
    }

    @Mapping(types = 驾驶员身份信息采集上报, desc = "驾驶员身份信息采集上报")
    public void T0702(T0702 message, Session session) {
        session.response(message);
    }

    @Mapping(types = CAN总线数据上传, desc = "CAN总线数据上传")
    public void T0705(T0705 message, Session session) {
    }

    @Mapping(types = 多媒体事件信息上传, desc = "多媒体事件信息上传")
    public void T0800(T0800 message, Session session) {
    }

    @Async
    @Mapping(types = 多媒体数据上传, desc = "多媒体数据上传")
    public JTMessage T0801(T0801 message, Session session) {
        if (message.getPacket() == null) {
            T0001 result = new T0001();
            result.copyBy(message);
            result.setMessageId(JT808.平台通用应答);
            result.setSerialNo(session.nextSerialNo());

            result.setResponseSerialNo(message.getSerialNo());
            result.setResponseMessageId(message.getMessageId());
            result.setResultCode(T0001.Success);
            return result;
        }
        fileService.saveMediaFile(message);
        T8800 result = new T8800();
        result.setMediaId(message.getId());
        return result;
    }

    @Mapping(types = 存储多媒体数据检索应答, desc = "存储多媒体数据检索应答")
    public void T0802(T0802 message, Session session) {
        session.response(message);
    }

    @Mapping(types = 摄像头立即拍摄命令应答, desc = "摄像头立即拍摄命令应答")
    public void T0805(T0805 message, Session session) {
        session.response(message);
    }

    @Mapping(types = 数据上行透传, desc = "数据上行透传")
    public void T0900(T0900 message, Session session) {
    }

    @Mapping(types = 数据压缩上报, desc = "数据压缩上报")
    public void T0901(T0901 message, Session session) {
    }

    @Mapping(types = 终端RSA公钥, desc = "终端RSA公钥")
    public void T0A00(T0A00_8A00 message, Session session) {
        session.response(message);
    }
}